import { init, withCancelToken, makeRequest, RequestArgs } from './utils';
import Axios from 'axios';
import { AxiosVue3Config } from './axiosVue3Plugin';

/**
 * 
 * @param options you can also use method chaining to do configuration.
 * 
 * e.g., const latestAxios = latestAxios().axiosInstance(xxx).autoRetry(xxx).cancelWhenUnmounted(xxx)
 * 
 * @returns a wrapped axios instance with latest mode.
 * 
 * It provide exactly same api with axios to make request.
 * 
 * It make request at latest mode, that is when a new request comes, all previous request will be cancelled.
 * 
 * It also provide reactive `loading`, `data` and `error` properties.
 * 
 * It provide `cancel` method to cancel current request.
 * 
 */
export const latestAxios = <T = any>(options: AxiosVue3Config = {}) => {
  const { config, state, axios, axiosReadonly, handler } = init<T>(options);

  handler.run = <R>(args: RequestArgs): Promise<R> => {
    state.latestId += 1;
    const currentId = state.latestId;
    state.loading = true;
    axios.loading = true;
    axios.cancel();
    axios.error = undefined;
    state.source = Axios.CancelToken.source();
    const argsWithCancelToken = withCancelToken(args, state.source.token);
    const instance = config.axiosInstance;
    return new Promise<R>((resolve, reject) => {
      let triedCount = 0;
      const onSuccess = (res: R): void => {
        if (currentId === state.latestId) {
          state.loading = false;
          axios.loading = false;
          if (res) {
            axios.data = (res as any).data;
          }
          resolve(res);
        }
      }
      const onError = (err: any): void => {
        if (currentId !== state.latestId) {
          return;
        }
        if (Axios.isCancel(err)) {
          if (currentId === state.latestId) {
            state.loading = false;
            axios.loading = false;
          }
        } else {
          if (config.autoRetry && config.autoRetry.maxCount > triedCount) {
            triedCount += 1;
            if (config.autoRetry.interval > 0) {
              state.timer = setTimeout(() => {
                makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError);
              }, config.autoRetry.interval)
            } else {
              makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError);
            }
          } else {
            state.loading = false;
            axios.loading = false;
            axios.error = err;
            reject(err);
          }
        }
      }
      makeRequest<R>(instance, argsWithCancelToken).then(onSuccess).catch(onError);
    })
  }

  return axiosReadonly;
}